// for 2.9黑白E029A01-FPC-A1 with
// ESP32C3-Supermini, seeed_xiao_esp32c3 and ...
#include <Arduino.h>
#include <time.h>
#include <WiFi.h>

#include <GxEPD2_BW.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
#include <Fonts/FreeMonoBold24pt7b.h>

// for 2.9黑白E029A01-FPC-A1 with nologo tech ESP32C3-Supermini
GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS=D8 SS*/ 7, /*DC=D3*/ 3, /*RST=D4*/ 10, /*BUSY=D2*/ 5)); // 2.9黑白 E029A01-FPC-A1

#define ENABLE_GxEPD2_GFX 0
#define LOG               USBSerial // USBSerial串口for ESP32C3-Supermini
String glb_SSID = "XXX";
String glb_PSWD = "XXX";
String glb_Slog;
struct tm glb_tm;

bool updateTime(char *pszTime, int tlen, char *pszDate, int dlen)
{
    for (int i = 0; i < 10; i++)
    {
        if (getLocalTime(&glb_tm))
            break;
        LOG.print(F("fail to obtain local time."));
    }

    if (glb_tm.tm_year < (2024 - 1900))
    {
        LOG.print(F("invalid local time."));
        return false;
    }

    if (NULL != pszTime)
    {
        strftime(pszTime, tlen, "%R", &glb_tm);
        LOG.println(pszTime);
    }
    if (NULL != pszDate)
    {
        strftime(pszDate, dlen, "%F %A", &glb_tm);
        LOG.println(pszDate);
    }
    return true;
}
// 初始化显示
void showInitScreen(void)
{
    // full window mode is the initial mode, set it anyway
    LOG.println("showInitScreen");
    display.init(115200, true, 2, false);
    display.setRotation(1);

    display.fillScreen(GxEPD_WHITE);
    display.setFullWindow();

    // drawLine
    display.drawLine(0, 16, display.width(), 16, GxEPD_BLACK);
    display.drawLine(0, 108, display.width(), 108, GxEPD_BLACK);
    display.drawLine(180, 18, 180, 106, GxEPD_BLACK);

    display.display(false); // full update
}
// 局部刷新时间区域
void showPartialTime(char *pszTime)
{
    // Partial mode
    LOG.println("showPartialTime");
    display.setFont(&FreeMonoBold24pt7b);
    display.setTextColor(GxEPD_BLACK);
    display.setFullWindow();

    display.fillRect(0, 18, 178, 80, GxEPD_WHITE);
    display.setCursor(22, 77);
    display.print(pszTime);
    display.displayWindow(0, 18, 178, 80);
    display.powerOff();
}
// 局部刷新温度区域
void showPartialTemp(float temp1, float temp2)
{
    char sztemp1[8];
    char sztemp2[8];
    snprintf(sztemp1, 8, "I %.1f", temp1);
    snprintf(sztemp2, 8, "O %.1f", temp2);

    // Partial mode
    LOG.println("showPartialTemp");
    display.setFont(&FreeMonoBold12pt7b);
    display.setTextColor(GxEPD_BLACK);
    display.setFullWindow();

    display.fillRect(182, 18, 100, 80, GxEPD_WHITE);
    display.setCursor(195, 50);
    display.print(sztemp1); // indoor
    display.setCursor(195, 90);
    display.print(sztemp2); // outdoor
    display.displayWindow(182, 18, 100, 80);
    display.powerOff();
}
// 局部刷新日期区域
void showPartialDate(char *pszDate)
{
    // Partial mode
    LOG.println("showPartialDate");
    display.setFont(&FreeMonoBold9pt7b);
    display.setTextColor(GxEPD_BLACK);
    display.setFullWindow();

    display.fillRect(0, 0, display.width(), 14, GxEPD_WHITE);
    display.setCursor(10, 12);
    display.print(pszDate);
    display.displayWindow(0, 0, display.width(), 14);
    display.powerOff();
}
// 全局刷新所有信息
void showFullScreen(char *pszTime, char *pszDate, float temp1, float temp2)
{
    LOG.println("showFullScreen");
    display.fillScreen(GxEPD_WHITE);
    display.setTextColor(GxEPD_BLACK);
    display.setFullWindow();

    // drawLine
    display.drawLine(0, 16, display.width(), 16, GxEPD_BLACK);
    display.drawLine(0, 108, display.width(), 108, GxEPD_BLACK);
    display.drawLine(180, 18, 180, 106, GxEPD_BLACK);

    // Time
    display.setFont(&FreeMonoBold24pt7b);
    display.setCursor(22, 77);
    display.print(pszTime);

    // Date
    display.setFont(&FreeMonoBold9pt7b);
    display.setTextSize(1);
    display.setCursor(10, 12); // x, y
    display.print(pszDate);

    // slog
    display.setFont(&FreeMonoBold9pt7b);
    display.setCursor(10, 124); // x, y
    display.print(glb_Slog.c_str());

    // temp
    char sztemp1[8];
    char sztemp2[8];
    snprintf(sztemp1, 8, "I %.1f", temp1);
    snprintf(sztemp2, 8, "O %.1f", temp2);

    display.setFont(&FreeMonoBold12pt7b);
    display.setCursor(195, 50);
    display.print(sztemp1); // indoor
    display.setCursor(195, 90);
    display.print(sztemp2); // outdoor

    display.display(false); // full update
    display.powerOff();
}

bool connectNewWifi(void)
{
    WiFi.mode(WIFI_STA);                            // 切换为STA模式
    WiFi.setAutoConnect(true);                      // 设置自动连接
    WiFi.begin(glb_SSID.c_str(), glb_PSWD.c_str()); // 连接上一次连接成功的wifi

    LOG.print(F("Connecting to WiFi: "));
    LOG.println(glb_SSID);

    int count = 0;
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        LOG.print(".");
        if (count > 20)
        {
            // 如果20秒内没有连上，就开启Web配网 可适当调整这个时间
            LOG.println(F("\r\nConnect failed!"));
            return false; // 失败退出，防止无限初始化
        }
        count++;
    }

    if (WiFi.status() == WL_CONNECTED)
    {
        // 如果连接上，就输出IP信息 防止未连接上break后会误输出
        LOG.print(F("WiFi Connected, IP: "));
        LOG.println(WiFi.localIP()); // 打印esp8266的IP地址
    }

    return true;
}

void setup(void)
{
    glb_Slog = String("Impossible is nothing!");

    LOG.begin(115200);
    LOG.println("setup, cszhaoqm 20230513 via PlatformIO");

    showInitScreen();
    connectNewWifi();
    configTime(8 * 60 * 60, 0, "ntp1.aliyun.com");

    // update time/date
    char szTime[8];
    char szDate[32];
    updateTime(szTime, 8, szDate, 32);

    // update temp

    // update screen
    showFullScreen(szTime, szDate, 22.8, 25.6);

    WiFi.disconnect(true);
    WiFi.mode(WIFI_OFF);

    LOG.println("setup done!");
}

unsigned long lastHeartBeat = 0;
void loop(void)
{

    if ((millis() - lastHeartBeat) > 60 * 1000)
    {
        // update time/date
        char szTime[8];
        char szDate[32];
        updateTime(szTime, 8, szDate, 32);

        // update temp

        if ((glb_tm.tm_min % 20) == 0)
        {
            showFullScreen(szTime, szDate, 25.8, 27.6);
        }
        else
        {
            showPartialTime(szTime);
            if ((glb_tm.tm_min % 10) == 0)
                showPartialTemp(30.2, 32.9);
        }
        lastHeartBeat = millis();
    }
    delay(100);
}
